package cn.net.shuxing.yifu.common.front.controller;

import cn.net.shuxing.yifu.common.bean.RechargeHistory;
import cn.net.shuxing.yifu.common.bean.User;
import cn.net.shuxing.yifu.common.constant.PaymentStatus;
import cn.net.shuxing.yifu.common.dto.AlipayNotifyParam;
import cn.net.shuxing.yifu.common.dto.BaseDto;
import cn.net.shuxing.yifu.common.service.RechargeHistoryService;
import cn.net.shuxing.yifu.common.service.UserService;
import cn.net.shuxing.yifu.common.util.JedisUtil;
import cn.net.shuxing.yifu.common.util.OrderInfoUtil2_0;
import cn.net.shuxing.yifu.common.util.RedisKeyCreator;
import cn.net.shuxing.yifu.config.AlipayConfig;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import cn.net.shuxing.yifu.common.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
@Slf4j
public class AlipayController {

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private RechargeHistoryService rechargeHistoryService;

    @Autowired
    private UserService userService;

    @Autowired
    private JedisUtil jedisUtil;

    private ExecutorService executorService = Executors.newFixedThreadPool(20);

    /**
     * 调用支付宝为运单付款的Controller (安卓版)
     *
     * @return 支付宝生成的订单信息
     */
    @ApiOperation(value = "调起付款", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "totalFee", value = "总价", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "Integer", paramType = "query")
    })
    @RequestMapping("/front/pay/alipay")
    public BaseDto submitPayment(String totalFee, Integer userId, HttpServletResponse response) throws UnsupportedEncodingException, AlipayApiException {

//        totalFee = "0.02";
        //Jedis //jedis = jedisUtil.getJedis();
        String s = jedisUtil.get(RedisKeyCreator.rechargeBaseCount());
        //jedisUtil.close();
        BigDecimal baseCount = new BigDecimal(0);
        if (StringUtils.hasText(s))
            baseCount = new BigDecimal(s);
        if (baseCount.compareTo(new BigDecimal(totalFee)) > 0)
            return BaseDto.error("充值金额至少为" + baseCount.floatValue());
        // 保存支付信息
        RechargeHistory rechargeHistory = new RechargeHistory();
        User user = userService.getByPrimaryKey(userId);
        if (user == null)
            return BaseDto.error("用户不存在");
        rechargeHistory.setUserId(userId);
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");//可以方便地修改日期格式
        String order_no = dateFormat.format(now);  //生成商户订单号，不可重复
        rechargeHistory.setOrderNo(order_no);
        rechargeHistory.setFee(totalFee);
        rechargeHistory.setSubject("用户充值");
        rechargeHistory.setPaymentStatus(PaymentStatus.UNPAID.name());
        String outTradeNo = order_no + UUID.randomUUID().toString().substring(36 - order_no.length());
        rechargeHistory.setOutTradeNo(outTradeNo);
        rechargeHistoryService.insert(rechargeHistory);


        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APP_ID, AlipayConfig.APP_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGN_TYPE);
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("我是测试数据");
        model.setSubject(rechargeHistory.getSubject());
        model.setOutTradeNo(rechargeHistory.getOutTradeNo());
        model.setTimeoutExpress("30m");
        model.setTotalAmount(rechargeHistory.getFee());
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl("http://47.98.205.250/front/notify/alipay");
        AlipayTradeAppPayResponse alipayTradeAppPayResponse = null;
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            alipayTradeAppPayResponse = alipayClient.sdkExecute(request);
            System.out.println(alipayTradeAppPayResponse.getBody());//就是orderString 可以直接给客户端请求，无需再做处理。
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return BaseDto.ok(alipayTradeAppPayResponse.getBody());
    }

    /**
     * 订单付款支付宝 异步通知付款状态的Controller(安卓版)
     *
     * @param request
     * @return
     */
    @ApiOperation(value = "支付宝异步通知")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "out_trade_no", value = "总价", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "userId", value = "用户id", required = true, dataType = "Integer", paramType = "query")
    })
    @SuppressWarnings("rawtypes")
    @RequestMapping(value = "/front/notify/alipay")
    @ResponseBody
    public String callback(HttpServletRequest request) {
        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        System.out.println("支付宝回调，" + paramsJson);
        try {
//            AlipayConfig alipayConfig = new AlipayConfig();// 支付宝配置
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY,
                    AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
            if (signVerified) {
                log.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
//                this.check(params);
                // 另起线程处理业务
                executorService.execute(() -> {
                    AlipayNotifyParam param = buildAlipayNotifyParam(params);
                    String trade_status = param.getTradeStatus();
                    // 支付成功
                    if (trade_status.equals("TRADE_SUCCESS")
                            || trade_status.equals("TRADE_FINISHED")) {
                        // 处理支付成功逻辑
                        try {
                            /*
                                // 处理业务逻辑。。。
                                myService.process(param);
                            */

                            String out_trade_no = params.get("out_trade_no");
                            RechargeHistory rechargeHistory = new RechargeHistory();
                            rechargeHistory.clearCreateTimeAndUpdateTime();
                            rechargeHistory.setOutTradeNo(out_trade_no);
                            rechargeHistory = rechargeHistoryService.get(rechargeHistory);

                            if (!rechargeHistory.getPaymentStatus().equals(PaymentStatus.PAID.name()) && !rechargeHistory.getPaymentStatus().equals(PaymentStatus.ClOSED.name())) {
                                // 修改
                                RechargeHistory rechargeHistoryUpd = new RechargeHistory();
                                rechargeHistoryUpd.clearCreateTimeAndUpdateTime();
                                rechargeHistoryUpd.setOutTradeNo(out_trade_no);
                                rechargeHistoryUpd = rechargeHistoryService.get(rechargeHistoryUpd);
                                // 设置状态
                                rechargeHistoryUpd.setPaymentStatus(PaymentStatus.PAID.name());
                                // 设置创建时间
                                rechargeHistoryUpd.setCallbackTime(LocalDateTime.now());
                                // 设置内容
                                rechargeHistoryUpd.setCallbackContent(params.toString());
                                rechargeHistoryService.update(rechargeHistoryUpd);
                            }
                        } catch (Exception e) {
                            log.error("支付宝回调业务处理报错,params:" + paramsJson, e);
                        }
                    } else {
                        log.error("没有处理支付宝回调业务，支付宝交易状态：{},params:{}", trade_status, paramsJson);
                    }
                });
                // 如果签名验证正确，立即返回success，后续业务另起线程单独处理
                // 业务处理失败，可查看日志进行补偿，跟支付宝已经没多大关系。
                return "success";
            } else {
                log.info("支付宝回调签名认证失败，signVerified=false, paramsJson:{}", paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            log.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}", paramsJson, e.getMessage());
            return "failure";
        }
    }

    // 将request中的参数转换成Map
    private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<String, String>();

        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();

        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;

            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.toString().substring(1));
            } else {
                retMap.put(name, "");
            }
        }

        return retMap;
    }

    private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) {
        String json = JSON.toJSONString(params);
        return JSON.parseObject(json, AlipayNotifyParam.class);
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     *
     * @param params
     * @throws AlipayApiException
     */
//    private void check(Map<String, String> params) throws AlipayApiException {
//        String outTradeNo = params.get("out_trade_no");
//
//        // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
//        Order order = getOrderByOutTradeNo(outTradeNo); // 这个方法自己实现
//        if (order == null) {
//            throw new AlipayApiException("out_trade_no错误");
//        }
//
//        // 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
//        long total_amount = new BigDecimal(params.get("total_amount")).multiply(new BigDecimal(100)).longValue();
//        if (total_amount != order.getPayPrice().longValue()) {
//            throw new AlipayApiException("error total_amount");
//        }
//
//        // 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
//        // 第三步可根据实际情况省略
//
//        // 4、验证app_id是否为该商户本身。
//        if (!params.get("app_id").equals(AlipayConfig.APP_ID)) {
//            throw new AlipayApiException("app_id不一致");
//        }
//    }


//    public String paymentNotify(@RequestParam Map<String, String> paramMap, HttpServletRequest request) throws AlipayApiException {
//        Map<String,String> params = new HashMap<String,String>();
//        Map requestParams = request.getParameterMap();
//        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
//            String name = (String) iter.next();
//            String[] values = (String[]) requestParams.get(name);
//            String valueStr = "";
//            for (int i = 0; i < values.length; i++) {
//                valueStr = (i == values.length - 1) ? valueStr + values[i]
//                        : valueStr + values[i] + ",";
//            }
//            //乱码解决，这段代码在出现乱码时使用。
//            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
//            params.put(name, valueStr);
//        }
//        boolean flag = AlipaySignature.rsaCheckV1(params, AlipayConfig.ALIPAY_PUBLIC_KEY, "utf-8", AlipayConfig.SIGN_TYPE);
//
//        if (!flag) {
//            return "fail";
//        }
//        // 判断结束
//        String trade_status = paramMap.get("trade_status");
//        if ("TRADE_SUCCESS".equals(trade_status) || "TRADE_FINISHED".equals(trade_status)) {
//            // 查单据是否处理
//            String out_trade_no = paramMap.get("out_trade_no");
//            RechargeHistory rechargeHistory = new RechargeHistory();
//            rechargeHistory.clearCreateTimeAndUpdateTime();
//            rechargeHistory.setOutTradeNo(out_trade_no);
//            rechargeHistory = rechargeHistoryService.get(rechargeHistory);
//
//            if (rechargeHistory.getPaymentStatus().equals(PaymentStatus.PAID.name()) || rechargeHistory.getPaymentStatus().equals(PaymentStatus.ClOSED.name())) {
//                return "fail";
//            } else {
//                // 修改
//                RechargeHistory rechargeHistoryUpd = new RechargeHistory();
//                rechargeHistoryUpd.clearCreateTimeAndUpdateTime();
//                rechargeHistoryUpd.setOutTradeNo(out_trade_no);
//                rechargeHistoryUpd = rechargeHistoryService.get(rechargeHistoryUpd);
//                // 设置状态
//                rechargeHistoryUpd.setPaymentStatus(PaymentStatus.PAID.name());
//                // 设置创建时间
//                rechargeHistoryUpd.setCallbackTime(LocalDateTime.now());
//                // 设置内容
//                rechargeHistoryUpd.setCallbackContent(paramMap.toString());
//                rechargeHistoryService.update(rechargeHistoryUpd);
//                return "success";
//            }
//        }
//        return "fail";
//    }

}
